#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define NUM 1024
#define SIZE 32
#define SEP " "

//获取用户输入的命令行字符串
char cmd_line[NUM];
//将获取的字符串一个个分割开来
char* g_argv[SIZE];

// 环境变量的buffer 目的是新增的环境变量不被下一次memset清空
char g_tmpenv[64];

// shell运行原理: 父进程解析命令 等待子进程执行命令
int main()
{
   // extern char** environ;//debug

    //命令行解释器是一个常驻内存的进程(直到用户退出)
    while (1)
    {
        //显示提示信息
        printf("[root@localhost myshell]# ");
        fflush(stdout);
        memset(cmd_line, '\0', sizeof cmd_line);
        ///获取用户输入的命令行字符串 "ls -a -l -i"
        if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
        {
            continue;
        }
        //"ls -a -l -i\n\0"==>//"ls -a -l -i\0\0" 删除换行符以更贴切xshell
        cmd_line[strlen(cmd_line) - 1] = '\0';
        //debug: printf("echo: %s\n", cmd_line);//是否成功获得命令行字符串
        
        //命令行字符串解析："ls -a -l " -> "ls" "-a" "-l"
        g_argv[0] = strtok(cmd_line, SEP); //第一次调用传入原始字符串

        //一些特殊扩展的选项
        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0)
        {
            g_argv[index++] = "--color=auto";
        }
        if (strcmp(g_argv[0], "ll") == 0)
        {
            g_argv[0] = "ls";
            g_argv[index++] = "-l";
            g_argv[index++] = "--color=auto";
        }

        while (g_argv[index++] = strtok(NULL, SEP)); //后续解析原始字符串,传入NULL

        //debug: 查看是否传给解析命令行字符串
        //for(index = 0; g_argv[index]; index++)
        //    printf("g_argv[%d]: %s\n", index, g_argv[index]);
        
        //内置命令/内建命令: 父进程（shell）自己执行的命令 本质是shell中的一个函数调用
        if (strcmp(g_argv[0], "cd") == 0)
        {
            if (g_argv[1] != NULL) 
                chdir(g_argv[1]); 

            continue;
        }
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
        {
            strcpy(g_tmpenv, g_argv[1]);
            putenv(g_tmpenv);
            //debug1: 查看是否传给添加env
            //int ret = putenv(g_tmpenv);
            //if (ret == 0) 
            //    printf("%s export success\n", g_argv[1]);
            // 
            //debug2: 显示所有env来查看是否传给添加env
            //for(int i = 0; environ[i]; i++)
            //    printf("%d: %s\n", i, environ[i]);
            continue;
        }

        //子进程执行获取到的命令
        pid_t id = fork();
        if (id == 0) 
        {
            //debug: 子进程被替换前是否拿到env
            //printf("child's TMPENV: %s\n", getenv("TMPENV"));
            //printf("child's PATH: %s\n", getenv("PATH"));
           
            //ls -a -l
            //execvpe(g_argv[0], g_argv, environ);//ok: 显示传env 但没必要
            execvp(g_argv[0], g_argv);//ok test.c成功获取env
            exit(1);
        }
        //father
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));
    }
}
